iOS生命周期 - Main函数

所有基于C编写的app的入口都是main函数,但iOS应用程序有点不同。不同就是你不需要为iOS应用程序而自己编写main函数,当你使用Xcode创建工程的时候就已经提供了。除非一些特殊情况,否则你不应该修改Xcode提供的main函数实现,在下图中我们可以看到Xcode为我们生成了一个main.m文件:

ocmain.m

UIApplicationMain

截图中默认的iOS程序就是从main函数开始执行的,但是在main函数中我们其实只能看到一个方法,这个方法内部是一个消息循环(相当于一个死循 环),因此运行到

1
UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]))

UIApplicationMain这个方法之后程序不会自动退出,而只有当用户手动关闭程序这个循环才结束。这个方法有四个参数:

  • 第一个参数和第二个参数其实就是main函数的参数,分别代表:参数个数、参数内容;
  • 第三个参数代表UIApplication类(或子类)字符串,这个参数默认为nil则代表默认为UIApplication类,用户 可以自定义一个类继承于这个类;如果为nil则等价于NSStringFromClass([UIApplication class]),大家可以自己试验,效果完全一样;UIApplication是单例模式,一个应用程序只有一个UIApplication对象或子对 象;
  • 第四个参数是UIApplication的代理类字符串,默认生成的是AppDelegate类,这个类主要用于监听整个应用程序生命 周期的各个事件,当UIApplication运行过程中引发了某个事件之后会调用代理中对应的方法;
1
2
3
4
5
6
int main(int argc, char *argv[])
{
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}

main函数的两个参数(int argc, char *argv[]),iOS中没有用到,包括这两个参数是为了与标准ANSI C保持一致。

UIApplicationMain函数,前两个参数和main函数一样,重点是后两个,官方说明是这样的:

// If nil is specified for principalClassName, the value for NSPrincipalClass from the Info.plist is used. If there is no
// NSPrincipalClass key specified, the UIApplication class is used. The delegate class will be instantiated using init.
UIKIT_EXTERN int UIApplicationMain(int argc, char argv[], NSString principalClassName, NSString *delegateClassName);

后两个参数分别表示程序的主要类(principal class)和代理类(delegate class)。如果主要类为nil,将从Info.plist中获取,如果Info.plist中不存在对应的key,则默认为UIApplication;如果代理类为nil,将在新建工程时创建。

根据UIApplicationMain函数,程序将进入AppDelegate.m,这个文件是Xcode新建工程时自动生成的。

上面实例代码中有一个很重要的函数UIApplicationMain,它主要是创建app的几个核心对象来处理以下过程:

  1. 从可用Storyboard文件加载用户界面
  2. 调用AppDelegate自定义代码来做一些初始化设置
  3. 将app放入Main Run Loop环境中来响应和处理与用户交互产生的事件

Swift中的Main函数

用OC语言创建的项目,一定会有一个main.m文件,里面有一个main函数,在这个函数中唤醒app。如果在app启动时放置断点,可以看到应用程序调用栈,main函数作为程序启动后的第二个函数被调用,然后在main函数中再启动UIApplication,并绑定AppDelegate。如下图:

ocmain

但是Swift项目中没有一个名为main.swift的文件,为什么app的入口没有了?官方文档的说法是这样的:

In Xcode, Mac templates default to including a “main.swift” file, but for iOS apps the default for new iOS project templates is to add @UIApplicationMain to a regular Swift file. This causes the compiler to synthesize a mainentry point for your iOS app, and eliminates the need for a “main.swift” file.

这段话的意思是,Swift项目中添加了@UIApplicationMain 到swift文件中,使得编译器合成了一个app入口,所以不需要main.swift文件。

swiftmain

可能苹果认为我们并不需要自行配置app入口,所以干脆简化了项目配置,使用更加简单的方式启动应用,但有时候我们可能需要自己配置入口,例如我们要创建一个UIApplication的子类时。

自定义UIApplicationMain

如果你希望通过自行配置入口的方式来创建一个UIApplication子类,首先定义一个UIApplication的子类:

1
2
3
4
5
6
7
8
9
import Foundation
import UIKit
class MyApplication: UIApplication {
override func sendEvent(event: UIEvent) {
super.sendEvent(event)
print("send event")
}
}

然后新建main.swift文件:

1
2
3
4
import Foundation
import UIKit
UIApplicationMain(Process.argc, Process.unsafeArgv, NSStringFromClass(myApplication), NSStringFromClass(AppDelegate))

在Appdelegate的最顶部声明@UIApplicationMain,表示该类是application的delegate。

如果不用该属性标识,另外一种做法是在main.swift中调用UIApplicationMain函数,设置delegate和application。以上我们就是根据这种方法来设置的。

去掉Appdelegate的@UIApplicationMain,我们就将main.swift设置为了入口。

UIApplicationMain的参数

以前OC的main函数的argv参数是一个字符串指针,打印出来后是应用程序的路径。

Swift中main函数的 Process.unsafeArgv 参数变成了一串十六进制数字,应该也是代表应用程序地址。

Demo下载

GitHub传送门